# -*- coding:utf-8 -*-

# for python 2.5.x
from __future__ import with_statement

import urllib
import urllib2
import time
import hashlib
import hmac
import random
import binascii
import ConfigParser
import urlparse
try:
    import json
except ImportError:
    import simplejson as json

FETCH_INTERVAL = 300 # 5min
TWEET_INTERVAL = 10
AND_STR = "&"
EQ_STR = "="
default_setting = {}
base_timeline = '1303620033'

api_t_qq = {
    'request_token': ('get', 'https://open.t.qq.com/cgi-bin/request_token'),
    'authorize': ('get', 'https://open.t.qq.com/cgi-bin/authorize'),
    'access_token': ('get', 'https://open.t.qq.com/cgi-bin/access_token'),
    'broadcast_timeline': ('get', 'http://open.t.qq.com/api/statuses/broadcast_timeline')
    }

api_t_sina = {
    'request_token': ('get', 'http://api.t.sina.com.cn/oauth/request_token'),
    'authorize': ('get', 'http://api.t.sina.com.cn/oauth/authorize'),
    'access_token': ('get', 'http://api.t.sina.com.cn/oauth/access_token'),
    'status_update': ('post', 'http://api.t.sina.com.cn/statuses/update.json')
    }

def init_oauth_header(consumer_key, access_token='', call_back='', nonce_len=32):
    oauth_header = {}

    # default setting
    oauth_header['oauth_consumer_key'] = consumer_key
    oauth_header['oauth_signature_method'] = 'HMAC-SHA1'
    oauth_header['oauth_timestamp'] = str(int(time.time()))
    oauth_header['oauth_nonce'] = get_rand_str(nonce_len)
    oauth_header['oauth_version'] = '1.0'
    if access_token:
        oauth_header['oauth_token'] = access_token
    if call_back:
        oauth_header['oauth_callback'] = call_back

    return oauth_header

def escape_str(str):
    return urllib.quote(str, "-_,~")

def get_base_str(method, uri, params):
    # params已经是经过urlencode的, 进行排序
    params_list = sorted(params.items(), \
            cmp=lambda x,y: cmp(x[0], y[0]) or cmp(x[1], y[1]))
    params_str = AND_STR.join([EQ_STR.join(i)\
            for i in params_list])
    params_str_enc = escape_str(params_str)

    # 对uri进行urlencode
    uri_enc = escape_str(uri)

    # 修改method
    method_up = method.upper()

    # 最终返回值
    ret_str = AND_STR.join([method_up, uri_enc, params_str_enc])
    return ret_str

def get_signature(key, base_str):
    h = hmac.new(key, base_str, hashlib.sha1)
    return binascii.b2a_base64(h.digest())[:-1]

def get_signature_key(key1, key2 = None):
    ret = ''
    if not key2:
        ret = "%s&" % key1
    else:
        ret = "%s&%s" % (key1, key2)

    # print out content
    if default_setting['debug']: print "in [get_signature_key]", ret
    return ret

def get_rand_str(len, default_str='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'):
    return ''.join([ random.choice(default_str) for i in range(len) ])

def get_sina_oauth_header(oauth_data, realm=''):
    auth_header = 'OAuth realm="%s"' % realm
    # Add the oauth parameters.
    if oauth_data:
        for k, v in oauth_data.iteritems():
            if k[:6] == 'oauth_':
                auth_header += ', %s="%s"' % (k, escape_str(v))

    if default_setting['debug']: print "in [get_sina_oauth_header]", auth_header
    return auth_header

def do_oauth_request_token(method, uri, consumer_data, call_back='null', query=True):
    # 获取oauth data, 但缺少oauth_signature
    oauth_data = init_oauth_header(consumer_data[0], '', call_back)

    # 对oauth data进行urlencode
    oauth_data_copy = oauth_data.copy()
    for k, v in oauth_data_copy.iteritems():
        oauth_data_copy[k] = escape_str(v)

    # 生成base string
    base_str = get_base_str(method, uri, oauth_data_copy)
    if default_setting['debug']: print "in [do_oauth_request_token]", base_str

    # 生成签名密钥
    signature_key = get_signature_key(consumer_data[1])

    # 生成签名
    signature_str = get_signature(signature_key, base_str)
    if default_setting['debug']: print "in [do_oauth_request_token]", signature_str

    # 将签名写入oauth data
    oauth_data['oauth_signature'] = signature_str

    # 生成请求数据
    if query:
        req = urllib2.Request("?".join([uri, urllib.urlencode(oauth_data)]))
    else:
        req = urllib2.Request(uri)
        auth_header = get_sina_oauth_header(oauth_data)
        req.add_header("Authorization", auth_header)

    # 访问
    response = urllib2.urlopen(req)
    result = response.read()
    response.close()

    return result

def do_oauth_authorize(uri, params):
    if 'oauth_token' in params:
        print 'Authorize at {0}'.format(uri + "?" + params['oauth_token'])
    check_code = raw_input("Input pin code:")
    return check_code.strip()

def do_oauth_access_token(method, uri, consumer_data, request_token_data, params, query=True):
    # 获取oauth data, 但缺少oauth_signature
    oauth_data = init_oauth_header(consumer_data[0])

    # 对oauth data进行urlencode
    oauth_data.update(params)
    oauth_data_copy = oauth_data.copy()
    for k, v in oauth_data_copy.iteritems():
        oauth_data_copy[k] = escape_str(v)

    # 生成base string
    base_str = get_base_str(method, uri, oauth_data_copy)
    if default_setting['debug']: print "in [do_oauth_access_token]", base_str

    # 生成签名密钥
    signature_key = get_signature_key(consumer_data[1], request_token_data[1])

    # 生成签名
    signature_str = get_signature(signature_key, base_str)
    if default_setting['debug']: print "in [do_oauth_access_token]", signature_str

    # 将签名写入oauth data
    oauth_data['oauth_signature'] = signature_str
    
    # 生成请求数据
    if query:
        req = urllib2.Request("?".join([uri, urllib.urlencode(oauth_data)]))
    else:
        req = urllib2.Request(uri)
        auth_header = get_sina_oauth_header(oauth_data)
        req.add_header("Authorization", auth_header)

    # 访问
    response = urllib2.urlopen(req)
    result = response.read()
    response.close()

    return result

def init_default_setting(fn_setting='setting.cfg', fn_secret='secret.cfg'):
    config = ConfigParser.ConfigParser()
    
    default_setting['last_tweet'] = '0'
    with open(fn_setting, 'r') as cfg_file:
        config.readfp(cfg_file)

        default_setting['debug'] = config.getboolean("info", "debug")
        default_setting['t_qq_consumer_key'] = config.get("info", "t_qq_consumer_key")
        default_setting['t_qq_consumer_secret'] = config.get("info", "t_qq_consumer_secret")
        default_setting['t_sina_consumer_key'] = config.get("info", "t_sina_consumer_key")
        default_setting['t_sina_consumer_secret'] = config.get("info", "t_sina_consumer_secret")       
        default_setting['t_qq'] = False
        default_setting['t_sina'] = False

        if config.has_section("info") and config.has_option("info", "web_proxy"):
            default_setting['web_proxy'] = config.get("info", "web_proxy")
        else:
            default_setting['web_proxy'] = ''

    with open(fn_secret, 'r') as cfg_file:
        config.readfp(cfg_file)
        
        # 判断是否有secret
        if config.has_section("secret"):
            token = config.get("secret", "t_qq_access_token")
            secret = config.get("secret", "t_qq_access_secret")
            # 得到t.qq的数据
            if token and secret:
                default_setting['t_qq'] = True
                default_setting['t_qq_access_token'] = token
                default_setting['t_qq_access_secret'] = secret

            token = config.get("secret", "t_sina_access_token")
            secret = config.get("secret", "t_sina_access_secret")
            # 得到t.sina的数据
            if token and secret:
                default_setting['t_sina'] = True
                default_setting['t_sina_access_token'] = token
                default_setting['t_sina_access_secret'] = secret

        # 判断是否有timeline
        if config.has_section("timeline") and config.has_option("timeline", "last_tweet"):
            default_setting['last_tweet'] = config.get("timeline", "last_tweet")
    
    if default_setting['debug']: print 'in [init_default_setting]', default_setting

def write_secret_cfg(fname="secret.cfg"):
    config = ConfigParser.ConfigParser()
    config.add_section("secret")
    config.set("secret", "t_qq_access_token", default_setting['t_qq_access_token'] or '')
    config.set("secret", "t_qq_access_secret", default_setting['t_qq_access_secret'] or '')
    config.set("secret", "t_sina_access_token", default_setting['t_sina_access_token'] or '')
    config.set("secret", "t_sina_access_secret", default_setting['t_sina_access_secret'] or '')

    # 写入文件
    with open(fname, 'w') as cfg_file:
        config.write(cfg_file)

def write_timeline(fname="secret.cfg"):
    config = ConfigParser.ConfigParser()
    config.read(fname)
    if not config.has_section("timeline"):
        config.add_section("timeline")
        
    config.set("timeline", "last_tweet", default_setting['last_tweet'])
    with open(fname, 'w') as config_file:
        config.write(config_file)
        

def process_oauth(domain, api_t, consumer_data):
    domain_key = 't_' + domain # t_qq or t_sina
    if domain_key not in default_setting or not default_setting[domain_key]:
        # 还没有拿到授权访问的token和key
        # 进行token的请求
        query = domain == 'qq' and True or False
        result = do_oauth_request_token(api_t['request_token'][0],
                                    api_t['request_token'][1],
                                    consumer_data,
                                    query)
        if default_setting['debug']: print "in [process_oauth]:", result
        dict_result = urlparse.parse_qs(result)

        # 存储token和secret，但未认证
        domain_access_token = 't_{0}_access_token'.format(domain)
        domain_access_secret = 't_{0}_access_secret'.format(domain)
        default_setting[domain_access_token] = dict_result['oauth_token'][0]
        default_setting[domain_access_secret] = dict_result['oauth_token_secret'][0]

        user_authorize_url = api_t['authorize'][1] + "?" + "oauth_token=" + default_setting[domain_access_token]
        print "Access the url to get code:", user_authorize_url
        check_code = raw_input("Check-code:").strip()
        # 拿到验证码
        params = {}
        params['oauth_verifier'] = check_code
        params['oauth_token'] = default_setting[domain_access_token]
        request_token_data = (default_setting[domain_access_token],
                              default_setting[domain_access_secret])
        # 换取access token 和 key
        result = do_oauth_access_token(api_t['access_token'][0],
                                       api_t['access_token'][1],
                                       consumer_data,
                                       request_token_data,
                                       params,
                                       query)
        if default_setting['debug']: print "in [process_oauth]:", result
        dict_result = urlparse.parse_qs(result)

        default_setting[domain_access_token] = dict_result['oauth_token'][0]
        default_setting[domain_access_secret] = dict_result['oauth_token_secret'][0]
        if default_setting[domain_access_token] and default_setting[domain_access_secret]:
            default_setting[domain_key] = True

            # 完成验证，得到授权，将token和key存起来
        write_secret_cfg()

    return True


def do_general_request(method, uri, consumer_data, access_token_data, params, query=True):
    # 获取oauth data, 但缺少oauth_signature
    oauth_data = init_oauth_header(consumer_data[0], access_token_data[0])

    # 对oauth data进行urlencode
    oauth_data.update(params)
    oauth_data_copy = oauth_data.copy()
    for k, v in oauth_data_copy.iteritems():
        oauth_data_copy[k] = escape_str(v)

    # 生成base string
    base_str = get_base_str(method, uri, oauth_data_copy)
    if default_setting['debug']: print "in [do_genral_request]", base_str

    # 生成签名密钥
    signature_key = get_signature_key(consumer_data[1], access_token_data[1])

    # 生成签名
    signature_str = get_signature(signature_key, base_str)
    if default_setting['debug']: print "in [do_genral_request]", signature_str

    # 将签名写入oauth data
    oauth_data['oauth_signature'] = signature_str
    
    # 生成请求数据
    if query:
        req = urllib2.Request("?".join([uri, urllib.urlencode(oauth_data)]))
    else:
        req_data = urllib.urlencode(params)
        if method.upper() == 'GET':
            req = urllib2.Request(uri + "?" + req_data)
        elif method.upper() == 'POST':
            req = urllib2.Request(uri, req_data)
        auth_header = get_sina_oauth_header(oauth_data)
        # sina的oauth头是不包括其它参数的，只跟oauth相关
        req.add_header("Authorization", auth_header)
        # 设置代理，根据需要
        if 'web_proxy' in default_setting and default_setting['web_proxy']:
            req.set_proxy(default_setting['web_proxy'], 'http')

    # 访问       
    response = urllib2.urlopen(req)
    result = response.read()
    response.close()

    return result

#########################################################################################

if __name__ == '__main__':
    # 初始化全局数据
    init_default_setting()

    # 初始化key
    qq_consumer_data = (default_setting['t_qq_consumer_key'],
                              default_setting['t_qq_consumer_secret'])
    sina_consumer_data = (default_setting['t_sina_consumer_key'],
                                default_setting['t_sina_consumer_secret'])

    # 进行oauth授权
    process_oauth('qq', api_t_qq, qq_consumer_data)
    process_oauth('sina', api_t_sina, sina_consumer_data)

    # 得到授权数据
    qq_access_token_data = (default_setting['t_qq_access_token'],
                         default_setting['t_qq_access_secret'])
    sina_access_token_data = (default_setting['t_sina_access_token'],
                              default_setting['t_sina_access_secret'])

    old_last_tweet = default_setting['last_tweet']
    new_last_tweet = ''

    while True:
        # 读取t.qq我发表的数据
        params = {'format': 'json', 'pageflag': '0', 'pagetime': '0',
                  'reqnum': '20', 'lastid': '0'}
        result = do_general_request(api_t_qq['broadcast_timeline'][0],
                              api_t_qq['broadcast_timeline'][1],
                              qq_consumer_data,
                              qq_access_token_data,
                              params)
        result_dict = json.loads(result)

        # 读取我发表的数据（原创）
        update_text_list = []
        if result_dict['ret'] == 0:
            info_list = result_dict['data']['info']
            new_last_tweet = info_list[0]['timestamp']
            for info in info_list:
                if info['type'] == 1 or info['type'] == '1':
                    if info['timestamp'] > int(old_last_tweet):
                        update_text_list.append(info['origtext'])
            old_last_tweet = new_last_tweet
            default_setting['last_tweet'] = new_last_tweet
            # 写入时间戳
            write_timeline()

        print "####################"
        print "update_text_list length:", len(update_text_list)

        # 推动到t.sina
        # 按时间反序
        update_text_list = reversed(update_text_list)
        for text in update_text_list:
            sina_update_params = {'source': sina_consumer_data[0],
                                  'status': text.encode('utf-8') }
            result = do_general_request(api_t_sina['status_update'][0],
                                       api_t_sina['status_update'][1],
                                       sina_consumer_data,
                                       sina_access_token_data,
                                       sina_update_params,
                                        False)
            # 避免发表过快，暂停一段时间
            time.sleep(TWEET_INTERVAL)

        # 避免拉取太快，暂停一段时间
        time.sleep(FETCH_INTERVAL)
    print "Ok"
